#!/usr/bin/env python 
# -*- coding: utf-8 -*-
#
#       odsDecryption.py - Mestrado em Engenharia de Segurança Informática
#       
#       Copyright 2013 Mário Candeias <198912832@alunos.ipbeja.pt>
#       
#       This program is an academic software.
#       Only for academic purposes. Under free GNU Licences.
#
#       Tested in files os Apache Open Office 4.0
#
class MyOutput():
################################################
### Necessary to print for file and stdout   ###
### at the same time.                        ###
################################################
#  -> http://tech.xster.net/tips/python-log-stdout-to-file/
    def __init__(self, logfile):
        self.stdout = sys.stdout
        self.log = open(logfile, 'w')
 
    def write(self, text):
        self.stdout.write(text)
        self.log.write(text)
        self.log.flush()
 
    def close(self):
        self.stdout.close()
        self.log.close()

class Printer(): 
################################################
### Print things to stdout on one line       ###
### dynamically.                             ###
################################################
    def __init__(self,data):
        sys.stdout.write("\r\x1b[K"+data.__str__())
#        sys.stdout.flush()
#coding: utf8
import sys
import zipfile
import hashlib
import base64
import datetime
from Crypto.Cipher import Blowfish
from passlib.utils import pbkdf2

def counter(wordlist): 
################################################
### Open the Wordlist file and count the     ###
### words inside.                            ###
################################################
    counter = 0
    with open(wordlist, 'r') as textf:
        for line in textf:
            counter += 1
        return counter

def getElements(rawFile):
################################################
### Parse all necessary elements.            ###
################################################
    lista = str(rawFile)[:-2] #--- Raw file to string

# -- Search all necessary elements
    searchCS = lista.split("checksum', ")
    checksum = searchCS[1].split(')')[0]
    searchIv = lista.split("initialisation-vector', ")
    iv = searchIv[1].split(')')[0]
    searchSalt = lista.split("salt', ")
    salt = searchSalt[1].split(')')[0]
    searchCSType = lista.split("checksum-type', ")
    checksum_type = searchCSType[1].split(')')[0]
    searchCSType = lista.split("algorithm-name', ")
    algorithm_name = searchCSType[1].split(')')[0] 
    iteration_count = 1024
    key_size = 16    
    
    return checksum, checksum_type, iv, salt, algorithm_name, iteration_count, key_size

def decryptMethod(odsFile, password, checksum, checksum_type, iv, salt, algorithm_name, iteration_count, key_size):
###################################################
### Read the 1st 1024 of encrypted contents.xml ###
### file and decrypt with password file.        ###
###################################################
    try:
        contentXML = odsFile.open("content.xml").read()
#        print "\n Content -------->%s<---------" % (contentXML)
    except KeyError, exc:
        print >> sys.stderr, "%s is not an encrypted OpenOffice file or content.xml is missing!\n --- The program finished!" % sys.argv[1]
        sys.exit()

    salt = base64.decodestring(salt)
    iv = base64.decodestring(iv)
        
    if algorithm_name.find("Blowfish CFB") > -1:
        passwordHash = hashlib.sha1(password).digest()
        key = pbkdf2.pbkdf2(passwordHash, salt, int(iteration_count), int(key_size))
        decipher = Blowfish.new(key=key, mode=Blowfish.MODE_CFB, IV=iv, segment_size=64)

        if len(contentXML) > 1024:
            pt = decipher.decrypt(contentXML[0:1024])
            #~ b64string = (base64.decodestring(contentXML))
            #~ decode_base64_and_inflate( b64string )
        else:
            print "The file is to small! --- content.xml < 1024bytes\n --- The program finished!"
            sys.exit()
    else:
        print "Wrong algorithm_name!"
    
    sys.stdout = sys.__stdout__
    checksum = base64.decodestring(checksum)
    
    if checksum_type.find("SHA1") > -1:
        checkChecksum = hashlib.sha1(pt[0:1024]).digest()
        #~ print "\n checkChecksum -------->%s<---------" % (checkChecksum)
        #~ print "\n pt -------->%s<---------" % (pt)
    else:
        checkChecksum = hashlib.sha256(pt[0:1024]).digest()

    if checkChecksum == checksum:
        return True
    else:
        return False
        
def decode_base64_and_inflate( b64string ):
    decoded_data = base64.b64decode( b64string )
    print zlib.decompress( decoded_data , -15)

def deflate_and_base64_encode( string_val ):
    zlibbed_str = zlib.compress( string_val )
    compressed_string = zlibbed_str[2:-4]
    return base64.b64encode( compressed_string )
    
    
def main():
    if len(sys.argv) < 3:
        print >> sys.stderr, "Usage: %s <RAW file> <Password file>" % sys.argv[0]
        sys.exit(1)
    try:
        rawFile = open(sys.argv[1]).readlines()
    except Exception, e:
        print >> sys.stderr, "\n %s is a valid Raw file! --- The program finished!" % sys.argv[1]
        sys.exit(2)
    try:
        wordlist = open(sys.argv[2]).read()
    except Exception, e:
        print >> sys.stderr, "%s is not a valid dictionary file! --- The program finished!" % sys.argv[2]
        sys.exit(3)
    odsFileName = "%s.ods" % sys.argv[1][:-4]
    try:
        odsFile = zipfile.ZipFile(odsFileName)
    except zipfile.BadZipfile:
        print >> sys.stderr, "\n %s is not an OpenOffice file! --- The program finished!" % sys.argv[1]
        sys.exit(2)

    checksum, checksum_type, iv, salt, algorithm_name, iteration_count, key_size = getElements(rawFile)

    print "Please wait! Checking the wordlist."
    total = counter(sys.argv[2]) 
    print 'There are %d words in the wordlist.' % total
#    raw_input('\nPress any key to continue!\n') 

    print "\nProgress Monitor  ---- Word in test"
    startTime = datetime.datetime.now() #--------Start Tracking Time-----
    progressMonitor = 0
    with open(sys.argv[2]) as mywordlist:
        for line in mywordlist:
            progressMonitor += 1
            output = "\r    %.3f%% " % (100 * float(progressMonitor)/float(total))
            word = line.strip()
            Printer("%s            %s " % (output, word))
            success = decryptMethod(odsFile, word, checksum, checksum_type, iv, salt, algorithm_name, iteration_count, key_size)
            if (success == True):
                print '\n\nSuccess!!! :-)\nLocated Password is: ', word
                #---- Finish Timing ------------
                finishTime = datetime.datetime.now() - startTime
                print "The program finished in :", finishTime
                #-----------------------------
                sys.exit()
                
    #---- Finish Timing ------------
    finishTime = datetime.datetime.now() - startTime
    print "\n\Password is not present in wordlist :-(\n\nThe program finished in :::", finishTime
    #-----------------------------
    rawFile.close()
    wordlist.close()
    
if __name__ == '__main__':
    main()
